﻿namespace WPForms.Common.Model
{
    using System;

    /// <summary>
    /// Represents an instance of a form.
    /// </summary>
    [System.Runtime.Serialization.DataContract(IsReference = true)]
    public class FormInstance
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FormInstance" /> class.
        /// </summary>
        public FormInstance()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FormInstance" /> class.
        /// </summary>
        /// <param name="name">The name of the form.</param>
        public FormInstance(string name)
        {
            this.Name = name;
        }

        /// <summary>
        /// Gets or sets the name of the form instance.
        /// </summary>
        [System.Runtime.Serialization.DataMember]
        [Description("The name of the form.")]
        public virtual string Name { get; set; }

        /// <summary>
        /// Gets or sets the unique identifier of the instance.
        /// </summary>
        [System.Runtime.Serialization.DataMember]
        public virtual int Id { get; set; }

        /// <summary>
        /// Gets or sets the server URL.
        /// </summary>
        [System.Runtime.Serialization.DataMember]
        [Description("The web service endpoint associated with this form. If set users will be able to upload forms.")]
        public virtual string ServerUrl { get; set; }

        /// <summary>
        /// Gets or sets the main section.
        /// </summary>
        [System.Runtime.Serialization.DataMember]
        public virtual FormSection MainSection { get; set; }

        /// <summary>
        /// Finds the element by key.
        /// </summary>
        /// <typeparam name="T">Type of the element to return.</typeparam>
        /// <param name="key">The key.</param>
        /// <returns>The element found.</returns>
        public T FindElementByKey<T>(string key) where T : FormElement
        {
            if (this.MainSection.Key == key)
            {
                return (T)(FormElement)this.MainSection;
            }

            return this.FindElementInSection<T>(this.MainSection, key);
        }

        /// <summary>
        /// Executes the given action for all fields in the form.
        /// </summary>
        /// <param name="action">The action to execute.</param>
        public void ForAllFields(Action<FormField> action)
        {
            this.ForAllFields(this.MainSection, action);
        }

        /// <summary>
        /// Executes the given action for all attachments in the form.
        /// </summary>
        /// <param name="action">The action to execute.</param>
        public void ForAllAttachments(Action<Attachment> action)
        {
            this.ForAllAttachments(this.MainSection, action);
        }

        /// <summary>
        /// Executes the given action for all fields in the given section.
        /// </summary>
        /// <param name="section">The section.</param>
        /// <param name="action">The action to execute.</param>
        private void ForAllAttachments(FormSection section, Action<Attachment> action)
        {
            if (section.Attachments != null)
            {
                foreach (var attachment in section.Attachments)
                {
                    action(attachment);
                }
            }

            foreach (var child in section.Children)
            {
                if (child is FormSection)
                {
                    this.ForAllAttachments((FormSection)child, action);
                }
            }
        }

        /// <summary>
        /// Executes the given action for all fields in the given section.
        /// </summary>
        /// <param name="section">The section.</param>
        /// <param name="action">The action to execute.</param>
        private void ForAllFields(FormSection section, Action<FormField> action)
        {
            foreach (var child in section.Children)
            {
                if (child is FormSection)
                {
                    this.ForAllFields((FormSection)child, action);
                }
                else if (child is FormField)
                {
                    action((FormField)child);
                }
            }
        }

        /// <summary>
        /// Finds the element in the given section.
        /// </summary>
        /// <typeparam name="T">Type of the element to return.</typeparam>
        /// <param name="section">The section.</param>
        /// <param name="key">The key.</param>
        /// <returns>The element found.</returns>
        private T FindElementInSection<T>(FormSection section, string key) where T : FormElement
        {
            foreach (var child in section.Children)
            {
                if (child.Key == key)
                {
                    return (T)child;
                }

                var childSection = child as FormSection;
                if (childSection != null)
                {
                    var found = this.FindElementInSection<T>(childSection, key);
                    if (found != null)
                    {
                        return found;
                    }
                }
            }

            return null;
        }
    }
}
